/**
 * elancom Corporation copy right 2012 - 2015  
 *   
 * $Header: https://192.168.1.224/svn/elancom/product/elancom-platform/trunk/src/main/java/com/elancom/framework/commons/json/serializer/MapSerializer.java 1034 2012-07-02 03:09:12Z lanzhouxue $
 * $Id: MapSerializer.java 1034 2012-07-02 03:09:12Z lanzhouxue $
 * $Author: lanzhouxue $
 * $Date: 2012-07-02 11:09:12 +0800 (Mon, 02 Jul 2012) $
 * $Revision: 1034 $
 */
package com.elancom.framework.commons.json.serializer;

import java.io.IOException;
import java.lang.reflect.Type;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;

/**
 * @author elancom
 */
public class MapSerializer implements ObjectSerializer {

	public static MapSerializer instance = new MapSerializer();

	@Override
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType) throws IOException {
		final SerializeWriter out = serializer.getWriter();

		if (object == null) {
			out.writeNull();
			return;
		}

		Map<?, ?> map = (Map<?, ?>) object;

		if (out.isEnabled(SerializerFeature.SortField)) {
			if ((!(map instanceof SortedMap)) && !(map instanceof LinkedHashMap)) {
				try {
					map = new TreeMap(map);
				} catch (final Exception ex) {
					// skip
				}
			}
		}

		if (serializer.containsReference(object)) {
			serializer.writeReference(object);
			return;
		}

		final SerialContext parent = serializer.getContext();
		serializer.setContext(parent, object, fieldName);
		try {
			out.write('{');

			Class<?> preClazz = null;
			ObjectSerializer preWriter = null;

			boolean first = true;

			if (out.isEnabled(SerializerFeature.WriteClassName)) {
				out.writeFieldName("@type");
				out.writeString(object.getClass().getName());
				first = false;
			}

			for (final Map.Entry entry : map.entrySet()) {
				Object value = entry.getValue();

				final Object entryKey = entry.getKey();

				if (entryKey == null || entryKey instanceof String) {
					String key = (String) entryKey;
					final List<PropertyFilter> propertyFilters = serializer.getPropertyFiltersDirect();
					if (propertyFilters != null) {
						boolean apply = true;
						for (final PropertyFilter propertyFilter : propertyFilters) {
							if (!propertyFilter.apply(object, key, value)) {
								apply = false;
								break;
							}
						}

						if (!apply) {
							continue;
						}
					}

					final List<NameFilter> nameFilters = serializer.getNameFiltersDirect();
					if (nameFilters != null) {
						for (final NameFilter nameFilter : nameFilters) {
							key = nameFilter.process(object, key, value);
						}
					}

					final List<ValueFilter> valueFilters = serializer.getValueFiltersDirect();
					if (valueFilters != null) {
						for (final ValueFilter valueFilter : valueFilters) {
							value = valueFilter.process(object, key, value);
						}
					}

					if (value == null) {
						if (!serializer.isEnabled(SerializerFeature.WriteMapNullValue)) {
							continue;
						}
					}

					if (!first) {
						out.write(',');
					}

					out.writeFieldName(key, true);
				} else {
					if (!first) {
						out.write(',');
					}

					serializer.write(entryKey);
					out.write(':');
				}

				first = false;

				if (value == null) {
					out.writeNull();
					continue;
				}

				final Class<?> clazz = value.getClass();

				if (clazz == preClazz) {
					preWriter.write(serializer, value, entryKey, null);
				} else {
					preClazz = clazz;
					preWriter = serializer.getObjectWriter(clazz);

					preWriter.write(serializer, value, entryKey, null);
				}
			}
		} finally {
			serializer.setContext(parent);
		}
		out.write('}');
	}
}
